require 'digest/sha1'

class User < ActiveRecord::Base
  # Virtual attribute for the unencrypted password
  attr_accessor :password

  belongs_to  :site
  belongs_to  :runner
  has_many    :groups

  validates_presence_of     :login
  validates_presence_of	    :email,			 :if => :not_student?
  validates_presence_of     :password,                   :if => :password_required?
  validates_presence_of     :password_confirmation,      :if => :password_required?
  validates_length_of       :password, :within => 4..40, :if => :password_required?
  validates_confirmation_of :password,                   :if => :password_required?
  validates_length_of       :login,    :within => 3..40
  validates_length_of       :email,    :within => 3..100, :if => :not_student?
  validates_uniqueness_of   :login, :email, :case_sensitive => false
  validates_inclusion_of    :role, :in => %w{ super coord lead student }, :message => "must be super, coord, lead, or student."
  before_save :encrypt_password

  has_many :permissions
  
  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  def self.authenticate(login, password)
    u = find_by_login(login) # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  # Encrypts some data with the salt.
  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def authenticated?(password)
    crypted_password == encrypt(password)
  end

  def remember_token?
    remember_token_expires_at && Time.now.utc < remember_token_expires_at 
  end

  # These create and unset the fields required for remembering users between browser closes
  def remember_me
    self.remember_token_expires_at = 2.weeks.from_now.utc
    self.remember_token            = encrypt("#{email}--#{remember_token_expires_at}")
    save(false)
  end

  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end
  
  def in_my_site?(object)
    site = self.site
    d = {"Runner" => lambda { |object| return object.group.site == site},
         "Group"  => lambda { |object| return object.site == site},
         "Site"   => lambda { |object| return object == site}}
    f = d.fetch(object.class.name, lambda { |object| return false})
    return f.call(object)
  end
  
  def in_my_groups?(object)
    groups = self.groups
    d = {"Group" => lambda { |object| return object },
         "Runner" => lambda { |object| return object.group }}
    for group in groups
      f = d.fetch(object.class.name, lambda { |object| return nil })
      if f.call(object) == group
        return true
      end
    end
    return false
  end
  
  def in_my_users?(object)
    role = self.role
    if object == self
      return true
    elsif role == 'coord' && object.role == 'lead'
      return true
    else
      return false
    end
  end
  
  def can(capability, object)
    # because i started using can without ? everywhere and i was too lazy to fix it
    return self.can?(capability, object)
  end
    
  def can?(capability, object)
    # Still need to see if the user is a teacher, admin, etc...
    role = self.role
    permission = Permission.find(:first, :conditions => {:name => capability, :role => role},
                                         :order => "level")
    if not permission.blank?
      
      d = { :global => lambda { |object| return true },
            :site   => lambda { |object| return self.in_my_site?(object) },
            :group  => lambda { |object| return true }, # we're turning off group level permissions for initial release
            :self   => lambda { |object| return self.in_my_users?(object) } }  
      return d.fetch(permission.level_name).call(object)
    else
      return false
    end
  end

  def is_not_student?
	  not (self.role == "student")
  end

  def get_runner
	  Runner.find(self.runner_id)
  end

  protected
    # before filter 
    def encrypt_password
      return if password.blank?
      self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
      self.crypted_password = encrypt(password)
    end
    
    def password_required?
      crypted_password.blank? || !password.blank?
    end

    def not_student?
      not role == "student"
    end

end

